home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Monster Media 1994 #2
/
Monster Media No. 2 (Monster Media)(1994).ISO
/
utils1
/
2m21src.zip
/
2MSYS.ASM
< prev
next >
Wrap
Assembly Source File
|
1994-05-31
|
94KB
|
2,248 lines
;┌───────────────────────────────────────────────────────────────────┐
;│ │
;│ ▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▄ ▒▒▒▒▄ ▒▒▒▒▄ │
;│ ▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒█ ▒▒▒▒▒▄ ▒▒▒▒▒█ │
;│ ▀▀▀▀▀▀▀▀▀▀▒▒▒▒█ ▒▒▒▒▒▒▄ ▒▒▒▒▒▒█ │
;│ ▒▒▒▒█ ▒▒▒▒▒▒▒▄▒▒▒▒▒▒▒█ │
;│ ▒▒▒▒█ ▒▒▒▒█▒▒▒▒▒█▒▒▒▒█ │
;│ ▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒█ ▒▒▒▒█ ▒▒▒█▀▒▒▒▒█ ▒▒▒▒▒▒▒▒▒▒▄ ▒▒▄ │
;│ ▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒█ ▒▒▒▒█ ▒█▀ ▒▒▒▒█ ▀▀▀▀▀▀▀▒▒█ ▒▒▒▒█ │
;│ ▒▒▒▒█▀▀▀▀▀▀▀▀▀▀▀ ▒▒▒▒█ ▀ ▒▒▒▒█ ▒▒█ ▒▒▄▀▒▒█ │
;│ ▒▒▒▒█ ▒▒▒▒█ ▒▒▒▒█ ▒▒▒▒▒▒▒▒▒▒█ ▀▀ ▒▒█ │
;│ ▒▒▒▒█ ▒▒▒▒█ ▒▒▒▒█ ▒▒█▀▀▀▀▀▀▀▀ ▒▒█ │
;│ ▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▄ ▒▒▒▒█ ▒▒▒▒█ ▒▒█ ▒▒█ │
;│ ▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒█ ▒▒▒▒█ ▒▒▒▒█ ▒▒▒▒▒▒▒▒▒▒▄ ▒▒▄ ▒▒▒▒▒▒▒▒▒▒▄ │
;│ ▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀ ▀▀▀▀ ▀▀▀▀ ▀▀▀▀▀▀▀▀▀▀ ▀▀ ▀▀▀▀▀▀▀▀▀▀ │
;│ │
;│ 2M 2.1 - (C) Mayo 1994 Ciriaco García de Celis. │
;│ │
;│ SOPORTE PARA DISQUETES CON MAYOR CAPACIDAD DE LA NORMAL │
;│ CREADOS POR LA UTILIDAD 2MF │
;│ │
;│ * * * Versión instalable desde el CONFIG.SYS * * * │
;│ │
;│ - Sólo para AT y superiores con controladora y unidades de alta. │
;│ - Programación directa del controlador de disquetes y del DMA. │
;│ - Soporte para sectores de más de 512 bytes (economizar GAPs). │
;│ - Mezcla de sectores de distinto tamaño en la misma pista. │
;│ - Ruptura del límite habitual de 80 pistas (incluido 360K). │
;│ - Emulación de sectores de 512 bytes en INT 13h. │
;│ - Función 5 (INT 13h) reforzada para formatear los nuevos discos. │
;│ - El soporte residente opera eficazmente bajo DOS y WINDOWS 3.X │
;│ │
;│ Emplear TASM /m5, TLINK y EXE2BIN para obtener un fichero SYS │
;│ │
;└───────────────────────────────────────────────────────────────────┘
.286 ; versión para AT o superior
; ------------ Macros de propósito general.
XPUSH MACRO regmem ; apilar lista de registros
IRP rm, <regmem>
PUSH rm
ENDM
ENDM
XPOP MACRO regmem ; desapilar lista de registros
IRP rm, <regmem>
POP rm
ENDM
ENDM
DELAY MACRO ; estados de espera
JMP SHORT $+2 ; para AT obsoleto
JMP SHORT $+2
ENDM
PMICRO MACRO ; retardo de aprox. 15,09 µs
LOCAL pmicro_iter ; (exactamente 18/1193180 sg.)
pmicro_iter: DELAY
IN AL,61h ; Esta macro puede ejecutarse
AND AL,10h ; repetitivamente (se apoya en
CMP AL,AH ; AX) para hacer retardos a
JE pmicro_iter ; través de la temporización
MOV AH,AL ; del refresco de la memoria
ENDM ; dinámica de los AT.
; ------------ Estructura de datos con información para cada unidad.
info_drv STRUC
maxs EQU 13 ; máximo 13 sectores físicos/pista
tipo_drv DB ? ; tipo de la disquetera (0 = no hay)
control2m_flag DB OFF ; a ON si 2M controla la unidad
cambio DB ON ; a ON indica cambio de soporte
version_fmt DB ? ; versión del formato de disco 2M
multi_io DB ? ; a 0 si posible acceso multi-sector
chk DB ? ; a 0 si checksum del sector 0 Ok
vunidad EQU THIS WORD
vunidad0 DB ? ; velocidad pista 0
vunidadx DB ? ; velocidad demás pistas
gap DB ? ; GAP entre sectores (leer/escribir)
sectpista DB ? ; sectores lógicos por pista
tabla_tsect DB maxs DUP (?) ; tamaños de sectores 1, 2, ..., N
tam_fat DB ? ; sectores/FAT en la unidad
ENDS
; ------------ Programa.
_PRINCIPAL SEGMENT
ASSUME CS:_PRINCIPAL, DS:_PRINCIPAL
ORG 0
ini_residente EQU $
DD -1 ; encadenamiento con otros drivers
tipo_drive DW 8000h ; palabra de atributo:
; bit 15 a 1: dispositivo caracteres
; bit 14 a 0: sin control IOCTL
DW estrategia ; rutina de estrategia
DW interrupcion ; rutina de interrupción
DB "2M$ " ; nombre del dispositivo
estrategia PROC FAR
MOV CS:pcab_pet_segm,ES
MOV CS:pcab_pet_desp,BX
RET
estrategia ENDP
interrupcion PROC FAR
CALL main ; tras instalar: XPUSH <DS, BX> y MOV BX,??
pcab_pet_segm DW ?
MOV DS,BX
DB 0BBh ; opcode de MOV BX,??
pcab_pet_desp DW ?
MOV WORD PTR [BX+3],8103h ; código de error
XPOP <BX, DS>
RET
interrupcion ENDP
; ****************************************
; * *
; * D A T O S R E S I D E N T E S *
; * *
; ****************************************
; ------------ Identificación estandarizada del programa.
program_id LABEL BYTE
segmento_real DW 0 ; segmento real donde será cargado
offset_real DW 0 ; offset real " " "
longitud_total DW 0 ; zona de memoria ocupada (párrafos)
info_extra DB 02h ; bits 0, 1 y 2-> 000: normal, con PSP
; 001: bloque UMB XMS
; 010: *.SYS
; 011: *.SYS formato EXE
; bit 7 a 1: «extension_id» definida
multiplex_id DB 0 ; número Multiplex de este TSR
vectores_id DW tabla_vectores
extension_id DW 0
DB "*##*"
autor_nom_ver DB "CiriSOFT:2M:2.1",0
DB 2 ; número de vectores de interrupción usados
tabla_vectores EQU $
vieja_i13 DB 13h ; INT 13h
ant_int13 LABEL DWORD ; dirección original
ant_int13_off DW 0
ant_int13_seg DW 0
DB 2Fh ; INT 2Fh
ant_int2F LABEL DWORD ; dirección original
ant_int2F_off DW 0
ant_int2F_seg DW 0
; ------------ Variables del programa.
info_ptr DW info_A ; punteros a datos de las unidades
DW info_B
id_sistema DB "2M-STV" ; identificación de disco 2M
unidad DB ? ; unidad física de disco en curso
numsect DW ? ; sectores a transferir
sectini DW ? ; primer sector DOS a transferir
cilindro DB ? ; cilindro del disco a acceder
cabezal DB ? ; cabezal a emplear
sector DB ? ; número de sector físico
sector_ini DB ? ; número de sector físico inicial
sector_fin DB ? ; número de sector físico final
seccion DB ? ; parte del sector físico en curso
secciones DB ? ; sectores lógicos a transferir
tsector DB ? ; LOG2 (tamaño de sector) - 7
buffer DW buffer_io ; puntero al buffer intermedio
buf_unidad DB ? ; unidad del sector en el buffer
buf_cilcab DW ? ; cilindro/cabezal de sector buffer
buf_sector DB ? ; número de sector en el buffer
status DB ? ; resultado de los accesos a disco
fdc_result DB 7 DUP (?) ; bytes de resultados del FDC
orden DB ? ; operación F_READ/F_WRITE/F_VERIFY
tab_ordenes DB F_READ
DB F_WRITE
DB F_VERIFY ; órdenes 2, 3 y 4
; --- Interpretación BIOS de los bits de ST1
lista_errs DB 4 ; 'sector not found'
DB 0
DB 10h ; 'bad CRC'
DB 8 ; 'DMA overrun'
DB 0
DB 4 ; 'sector not found'
DB 3 ; 'write-protect error'
DB 2 ; 'address mark not found'
DB 20h ; en otro caso: 'bad NEC'
info_A info_drv <> ; datos de A:
info_B info_drv <> ; datos de B:
; ***************************************
; * *
; * C O D I G O R E S I D E N T E *
; * *
; ***************************************
; ------------ Rutina de gestión de INT 2Fh.
ges_int2F PROC FAR
STI
CMP AH,CS:multiplex_id
JE preguntan
JMP CS:ant_int2F ; saltar al gestor de INT 2Fh
preguntan: CMP DI,1992h
JNE ret_no_info ; no llama alguien del convenio
MOV AX,ES
CMP AX,1492h
JNE ret_no_info ; no llama alguien del convenio
PUSH CS
POP ES ; sí llama: darle información
LEA DI,autor_nom_ver
ret_no_info: MOV AX,0FFFFh ; "entrada multiplex en uso"
IRET
ges_int2F ENDP
; ------------ Nueva rutina de gestión de INT 13h. Llama a la INT 13h
; original o a una nueva rutina de control para la
; lectura (AH=2), escritura (AH=3) y verificación (AH=4)
; según el tipo de disco introducido. Ante una función de
; formateo (AH=5) se entrega el control a la INT 13h
; original. Se detecta un posible cambio de disco y se
; retorna en ese caso con el correspondiente error.
ges_int13 PROC FAR
STI
CLD
PUSHF
CMP DL,2
JAE ges13bios ; no es disquetera A: ó B:
PUSH SI
CALL set_SI_drv
CMP CS:[SI].tipo_drv,2 ; ¿unidad 1.2M?
JE ges_2m
CMP CS:[SI].tipo_drv,4 ; ¿unidad 1.44/2.88M?
ges_2m: POP SI
JC ges13bios ; no es unidad de alta densidad
CMP AH,2
JB ges13bios ; no Read/Write/Verify/Format
CMP AH,5
JA ges13bios ; no Read/Write/Verify/Format
CALL detecta_cambio ; ¿cambio de disco?
JNC sin_cambio
POPF
STC ; hubo cambio:
MOV AX,600h
RET 2 ; retornar con error
sin_cambio: CMP AH,5
JNE dilucida ; no es orden de formateo
CALL leer_lin_camb
JNZ format_bios ; no hay disquete en la unidad
CMP AL,7Fh
JNE format_bios ; no es orden formateo de 2M
CMP SI,"2M"
JE format_2m ; es orden de formateo de 2M
format_bios: CLC
CALL set_flag_STV ; CF = 0 -> indicar no 2M
dilucida: PUSH SI
CALL set_SI_drv ; SI -> variables de la unidad
CMP CS:[SI].control2m_flag,OFF
POP SI
JE ges13bios ; la unidad la controla la BIOS
POPF
CALL control2m ; la controla 2M
RET 2
ges13bios: POPF
JMP CS:ant_int13 ; saltar al gestor de INT 13h
; --- Función de formateo implementada por 2M. En los
; disquetes creados con /M todas las pistas salvo
; la 0 deberían ser formateadas invocando INT 13h
; de manera directa (con CALL) para evitar que se
; ejecute cierto código de WINDOWS en el modo
; protegido que provoca errores al formatear. Antes
; de formatear la primera pista física del disco se
; invoca la función de formateo de la INT 13h
; original (con un error para provocar un rápido
; retorno) con objeto de informar al DOS y a todos
; los TSR previos del cambio de soporte.
format_2m: POPF
PUSH DS ; *
PUSHA ; **
PUSH CS
POP DS
MOV unidad,DL
CALL set_SI_drv
MOV cilindro,CH
MOV cabezal,DH
OR CH,DH
JNZ format_trx ; no es cilindro 0 y cabezal 0
PUSHA
MOV AL,1 ; formatear un sector (AH=5)
MOV CH,0
MOV DH,2 ; en cabezal 2 (incorrecto)
PUSHF
CALL ant_int13 ; avisar al DOS del nuevo disco
CLD ; mantener DF=0
STC
CALL reset_drv ; asegurar aceleración motor
POPA
CALL set_info ; características nuevo soporte
format_trx: CALL genera_info ; tabla de información formateo
CALL motor_ok ; asegurar que está en marcha
CALL seek_drv
CALL formatea_pista
PUSHF
CLC
CALL motor_off_cnt ; cuenta normal detención motor
POPF
CALL set_err
CALL set_bios_err ; no altera flags
POPA ; **
MOV AH,status
POP DS ; *
RET 2
ges_int13 ENDP
; ------------ A la entrada en DL se indica la unidad y a la salida se
; devuelve SI apuntando sus variables sin alterar flags.
set_SI_drv PROC
PUSHF
PUSH BX
MOV BL,DL
MOV BH,0
SHL BX,1
MOV SI,CS:[BX+OFFSET info_ptr]
POP BX
POPF
RET
set_SI_drv ENDP
; ------------ Si CF=1, indicar disquete 2M presente. A la
; entrada, DL indica la unidad de disco.
set_flag_STV PROC
PUSHA
CALL set_SI_drv
MOV AL,ON ; indicar 2M
JC tipo_stv_ok
MOV AL,OFF ; indicar no 2M
tipo_stv_ok: MOV CS:[SI].control2m_flag,AL
POPA
RET
set_flag_STV ENDP
; ------------ Devolver ZF=1 si cilindro y cabezal 0.
pista0? PROC
PUSH AX
MOV AL,cabezal
OR AL,cilindro
POP AX
RET
pista0? ENDP
; ------------ Devolver ZF=1 si la línea de cambio de disco está
; inactiva. A la entrada, DL contiene la unidad. El
; motor es puesto en marcha y, si no lo estaba ya, la
; variable que indica lo que resta para detenerlo
; es llevada a su valor normal, por lo que el disco no
; tardará mucho en detenerse (incluso sin quizá haber
; acelerado aún). En la práctica, invocando esta rutina
; desde INT 13h nunca será necesario arrancar el motor
; ya que el DOS ejecuta antes la función equivalente,
; la 16h, que lo pone en marcha. Es simplemente una
; medida de seguridad contra las BIOS «de marca».
leer_lin_camb PROC
PUSHA ; *
PUSH DS
PUSH 40h
POP DS
MOV AL,1
MOV CL,DL
SHL AL,CL ; bit de motor en 0..3
TEST DS:[3Fh],AL
JNZ rodando ; el motor ya está girando
CLC
CALL motor_off_cnt ; cuenta normal detención motor
rodando: MOV AH,DL
SHL AH,4
OR AH,AL ; AH = byte BIOS
SHL AL,4
OR AL,00001100b ; modo DMA, no hacer reset
OR AL,DL ; AL para reg. salida digital
MOV DX,3F2h
CLI
MOV DS:[3Fh],AH ; actualizar variable BIOS
OUT DX,AL ; arrancado motor en la unidad
ADD DX,5
DELAY
IN AL,DX ; leer línea de cambio de disco
STI
TEST AL,80h ; ZF=0 -> cambio de disco
POP DS
POPA ; *
RET
leer_lin_camb ENDP
; ------------ Determinar si ha habido cambio de disco y, en ese caso,
; si el nuevo disquete es de tipo 2M o no. El cambio de
; disco se detecta leyendo la línea de cambio de disco o
; chequeando la variable que indica si ha habido cambio
; o no (esta variable está a ON tras instalar 2M para
; forzar la detección del tipo de disco introducido; se
; pone en ON también si no se logra bajar la línea de
; cambio de disco por si fuera un soporte raro y la BIOS
; sí lo lograra -forzando así una detección posterior-).
detecta_cambio PROC
PUSHA ; *
CALL set_SI_drv ; SI -> variables de la unidad
CMP CS:[SI].cambio,ON ; ¿cambio de soporte?
MOV CS:[SI].cambio,OFF
JE hubo_cambio
CALL leer_lin_camb ; leer línea de cambio de disco
JNZ hubo_cambio
POPA
CLC ; no hay cambio de disco
RET
hubo_cambio: CLC
CALL set_flag_STV ; CF = 0 -> supuesto no 2M
XPUSH <DS, ES> ; **
MOV BX,90h
ADD BL,DL
PUSH 40h
POP DS
AND BYTE PTR [BX],255-16 ; densidad no determinada
XPUSH <CS, CS>
XPOP <DS, ES>
MOV unidad,DL
STC ; asegurar motor en marcha
CALL reset_drv
MOV cilindro,1
MOV cabezal,0
CALL seek_drv ; bajar línea cambio de disco
DEC cilindro
CALL seek_drv
CLC
CALL motor_off_cnt ; cuenta normal detención motor
CALL leer_lin_camb ; ¿bajada línea cambio disco?
JZ disco_dentro ; se pudo: hay disco dentro
MOV [SI].cambio,ON ; futura detección tipo disco
CLC ; NO indicar cambio de disco...
JMP fin_detecta ; ...para pasar control a BIOS
disco_dentro: PUSH DS
PUSH 40h
POP DS
MOV BYTE PTR DS:[41h],6 ; error 'media changed'
POP DS
CMP AH,5 ; ¿función de formateo?
JE fin_detecta_c ; no perder el tiempo
MOV buf_unidad,-1 ; invalidar buffer
MOV [SI].gap,20 ; GAP provisional
MOV CX,3 ; 3 intentos
intenta_io0: PUSH CX
CMP CX,2 ; CF=1 la 3ª vez (a 0 si CX<>1)
CALL reset_drv
MOV [SI].vunidad0,0 ; empezar con 500 Kbit/seg.
intenta_io: MOV cilindro,0
MOV cabezal,0
MOV sector,1 ; sector de arranque
MOV seccion,0
MOV secciones,1
MOV orden,F_READ
MOV DI,buffer
CALL direct_acceso
JNE otra_densidad ; es otra densidad de disco
POP CX
MOV BX,buffer
CALL set_info ; características nuevo soporte
CLC
JMP fin_detecta_c ; indicar cambio de disco
otra_densidad: MOV AL,[SI].vunidad0
INC AX ; próxima velocidad
CMP AL,3
JA otro_intento
MOV [SI].vunidad0,AL
JMP intenta_io ; probar otra velocidad
otro_intento: MOV [SI].vunidad0,0
POP CX
LOOP intenta_io0 ; reintento
fin_detecta_c: STC ; indicar cambio de disco
fin_detecta: XPOP <ES, DS> ; **
POPA ; *
RET
detecta_cambio ENDP
; ------------ Anotar la información del disquete si es de tipo 2M.
; A la entrada, DS:SI apunta a las variables de la unidad
; y ES:BX al sector de arranque del disco. Se actualiza
; también la variable BIOS de tipo de densidad (la BIOS
; no se da cuenta del cambio de disco y conviene ayudar).
set_info PROC
PUSHA
CALL calc_chk
JC set_info_exit ; no es disco 2M
MOV [SI].chk,AL ; anotar checksum
MOV [SI].version_fmt,CL ; y versión del formato
MOV DL,unidad
STC
CALL set_flag_STV ; CF = 1 -> indicar disco 2M
MOV AL,ES:[BX+22] ; tamaño de FAT
MOV [SI].tam_fat,AL
MOV CL,ES:[BX+65] ; CL a 0 si acceso multi-sector
MOV [SI].multi_io,CL
MOV AX,ES:[BX+66]
MOV [SI].vunidad,AX ; velocidad pista 0 / demás
MOV AL,ES:[BX+24]
MOV [SI].sectpista,AL ; sectores/pista
MOV DI,ES:[BX+72]
MOV AL,ES:[BX+DI+1] ; GAP de formateo
MOV AH,AL
AND CL,CL ; CL a 0 si acceso multi-sector
JZ gap_rw_ok ; GAP R/W para /F
ADD AH,190
MOV AL,11
MUL AH ; AX = (190+GAP)*11
SUB AX,2048+62
gap_rw_ok: SHR AL,1 ; GAP R/W para /M
MOV [SI].gap,AL
MOV CX,maxs
MOV DI,ES:[BX+74]
ADD DI,BX
LEA BX,[SI].tabla_tsect
genera_ts: MOV AL,ES:[DI]
MOV [BX],AL
INC BX
INC DI
LOOP genera_ts ; información estructura pistas
set_info_exit: MOV AL,[SI].vunidad0
SHL AL,6 ; velocidad en bits 7:6
OR AL,00010111b ; establecido otro medio físico
CMP [SI].tipo_drv,2
JA modo_ok ; es unidad de 3½
AND AL,11111000b
OR AL,00000101b ; 1.2 en 1.2
TEST AL,01000000b
JZ modo_ok
XOR AL,00100001b ; 360 en 1.2 y seek * 2
modo_ok: PUSH DS
MOV BX,90h
ADD BL,unidad
PUSH 40h
POP DS
AND BYTE PTR DS:[BX],8 ; respetar bit de 2.88M
OR DS:[BX],AL ; actualizar variable BIOS
POP DS ; con el tipo de densidad
POPA
RET
set_info ENDP
; ------------ Calcular el checksum de la zona vital del sector de
; arranque. A la entrada, ES:BX -> sector de arranque.
; A la salida, CF=1 si el disco no es 2M; de otro modo
; checksum en AL y versión del formato de disco en CL.
calc_chk PROC
XPUSH <SI, DI>
LEA DI,[BX+3] ; DI=BX+3
LEA SI,id_sistema
MOV CX,6
REP CMPSB ; comparar identificación
STC
JNE chk_ret ; el disco no es 2M
XOR AX,AX
MOV CL,ES:[BX+64] ; versión del formateador
CMP CL,6
JB chk_ok ; no usaba este checksum
MOV DI,ES:[BX+68]
chk_sum: DEC DI
ADD AL,ES:[BX+DI]
CMP DI,63
JA chk_sum
chk_ok: CLC
chk_ret: XPOP <DI, SI>
RET
calc_chk ENDP
; ------------ Determinar el tipo de error producido en el acceso.
set_err PROC
PUSHA
JNC err_ret ; no hay error
CMP status,0 ; ¿'status' ya asignado?
JNE err_retc ; no cambiarlo si es así
MOV AL,BYTE PTR fdc_result+1
AND AL,10110111b ; aislar condiciones de test
LEA BX,lista_errs
MOV CX,9
busca_err: MOV AH,[BX] ; código de error BIOS
SHL AL,1
JC err_ok ; es ese error
INC BX
LOOP busca_err ; buscar otro error
err_ok: OR status,AH
err_retc: STC ; condición de error
err_ret: POPA
RET
set_err ENDP
; ------------ Actualizar variables de error de la BIOS.
set_bios_err PROC
PUSHF ; *
PUSHA ; **
PUSH ES ; ***
PUSH 40h
POP ES
MOV DI,41h ; bytes de resultados del 765
LEA SI,status ; variable BIOS de status y 7
MOV CX,4 ; bytes: 4 palabras
REP MOVSW
POP ES ; ***
POPA ; **
POPF ; *
RET
set_bios_err ENDP
; ------------ Realizar lecturas, escrituras y verificaciones: rutina
; que sustituye el código de la BIOS para poder soportar
; los formatos 2M. La operación puede quedar dividida en
; tres fases: el fragmento anterior a la FAT2, la zona
; correspondiente a la FAT2 (se ignora la escritura y se
; simula su lectura leyendo la FAT1) y un último bloque
; ubicado tras la FAT2. El sector de arranque es emulado
; empleando el primer sector físico de la FAT2 (aunque en
; los discos de versión de formato anterior a la 7 se usa
; el sector de arranque verdadero -permitiendo escribirlo
; sólo si es válido-). En cualquier caso, si el número de
; cabezal tiene el bit 7 activo, se sobreentiende que el
; programa que llama soporta disquetes 2M y no se emula
; la FAT2 ni el sector de arranque, para permitirle
; acceder al código SuperBOOT. Las coordenadas de la BIOS
; se traducen a las unidades del DOS por mayor comodidad.
control2m PROC
PUSH DS ; *
PUSHA ; **
PUSH CS
POP DS
MOV unidad,DL
CALL set_SI_drv ; SI -> variables de la unidad
CMP [SI].chk,0
JE chk_valido ; checksum correcto en sector 0
MOV status,40h ; devolver 'Seek Error' al DOS
JMP exit_2m_ctrl
chk_valido: PUSH AX ; ***
MOV AH,0
MOV numsect,AX ; nº sectores
MOV AL,CH ; cilindro
SHL AL,1
MOV DL,DH
AND DH,01111111b
ADD AL,DH ; cabezal físico
MUL [SI].sectpista
ADD AL,CL ; sector
ADC AH,0
DEC AX ; AX = nº sector DOS
MOV sectini,AX
MOV DI,BX ; ES:DI -> dirección
POP BX ; ***
MOV BL,BH
MOV BH,0
MOV CL,[BX+OFFSET tab_ordenes-2]
MOV orden,CL
SHL DL,1
JC acceso_final ; cabezal >= 128: no emular
AND AX,AX ; ¿comienza en sector 0?
JNZ io_emula ; no
CMP [SI].version_fmt,7
JB boot_real ; no soportado BOOT virtual
MOV AL,[SI].tam_fat ; AH = 0
INC AX
MOV CX,1 ; sector BOOT emulado en
CALL ejecuta_io ; el primer sector FAT2
JNE fin_ctrl
boot_fin_op: DEC numsect
INC sectini
MOV AX,sectini
JMP io_emula
boot_real: CMP orden,F_WRITE
JNE io_emula
MOV BX,DI ; BOOT de 2M 1.3 y anteriores
CALL calc_chk
JC si_skip ; no es de tipo 2M
AND AL,AL
JZ io_emula ; lo es y con checksum correcto
si_skip: ADD DI,512
JMP boot_fin_op ; impedir estropicio de BOOT
io_emula: MOV CL,[SI].tam_fat
MOV CH,0 ; CX = primer sector FAT2 - 1
CMP AX,CX
JA en_fat2? ; ¿la operación afecta a FAT2?
CALL calc_iop ; calcular sectores antes FAT2
CALL ejecuta_io ; CX sectores desde AX
JNE fin_ctrl ; error
CMP numsect,0
JE fin_ctrl ; fin de la transferencia
en_fat2?: MOV AX,sectini
MOV CL,[SI].tam_fat
MOV CH,0
SHL CX,1 ; CX = último sector FAT2
CMP AX,CX
JA acceso_final ; la operación es tras la FAT2
CALL calc_iop ; sectores hasta fin de FAT2
CMP orden,F_WRITE
JNE emula_fat1
SHL CX,9 ; CX = CX * 512
ADD DI,CX ; ES:DI actualizado
JMP acceso_final
emula_fat1: MOV DL,[SI].tam_fat
MOV DH,0
SUB AX,DX ; leer de FAT1 y no de la FAT2
CALL ejecuta_io ; CX sectores desde AX
JNE fin_ctrl ; error
acceso_final: CMP numsect,0
JE fin_ctrl ; fin de la transferencia
MOV AX,sectini
MOV CX,numsect
CALL ejecuta_io
fin_ctrl: CLC
CALL motor_off_cnt ; cuenta normal detención motor
CALL set_bios_err ; actualizar variables BIOS
exit_2m_ctrl: POPA ; **
MOV AH,status
POP DS ; *
AND AH,AH
JZ st_ok ; resultado correcto (CF=0)
STC ; error
MOV AL,0 ; 0 sectores movidos
st_ok: RET
calc_iop: SUB CX,AX
INC CX ; CX sectores
CMP CX,numsect
JBE nsect_ok
MOV CX,numsect ; sólo quedan CX
nsect_ok: SUB numsect,CX
ADD sectini,CX
RET
control2m ENDP
; ------------ A la entrada, AX indica el sector inicial (coordenadas
; del DOS) y CX el número de sectores a procesar.
; * Definiciones: «Sector físico» es un sector del disco
; de 512, 1024 ó 2048 bytes (números de sector del 1 al N
; en la pista). Este sector físico está dividido en
; «secciones» de 512 bytes, constando por tanto de 1, 2 ó
; 4 secciones. «Sector virtual» es el número de sector
; del programa que llama a INT 13h, comprendido entre 1 y
; M. Esta estructura de N sectores por pista de distintos
; tamaños, se verifica en todo el disco con excepción del
; cabezal y cilindro 0 (con un formato más convencional
; de sectores de 512 bytes numerados de 1 a J, aunque no
; existen algunos de los intermedios que corresponden a
; la segunda copia de la FAT).
; * Primero se convierte el sector virtual (1..M) en su
; correspondiente físico (1..J en la pista 0 y 1..N en
; las demás), deduciendo qué porción de 512 bytes (o
; sección) es afectada. Un sector virtual (512 bytes)
; simulado suele ser parte de un sector físico de 2048
; bytes en muchos casos. Si dicho sector físico ya había
; sido leído al buffer en anteriores accesos, se extrae
; la sección necesaria. Si no, se carga del disco y se
; extrae dicho fragmento. El número de sectores virtuales
; que se solicitan (=secciones) permite realizar un bucle
; hasta completar la transferencia; el interleave 1:2 de
; los sectores físicos en /M permite acceder sector a
; sector sin pérdida de rendimiento. En el caso de la
; escritura, se estudia primero si hay varios sectores
; virtuales consecutivos que escribir, completando entre
; todos un sector físico: en ese caso, se prepara el
; mismo y se escribe sin más. En caso de que haya que
; modificar sólo una única sección de un sector físico,
; salvo si éste es de 512 bytes, no hay más remedio que
; cargarlo al buffer (realizar una prelectura),
; actualizar la sección correspondiente y volverlo a
; escribir.
; * En el formato /F se realiza una operación multisector
; si es posible y sin emplear el buffer intermedio (si
; bien podría ser preciso emplearlo con la primera y
; última sección); en los dos formatos de disco se hace
; la operación multisector en la primera pista. Las
; operaciones multisector puede que sea preciso
; dividirlas en tres fases: los sectores antes de una
; frontera de DMA, el que la cruza (que es transferido
; a través del buffer intermedio) y los que están detrás.
ejecuta_io PROC
MOV BX,AX ; AX = sector DOS inicial
MOV secciones,CL ; CX sectores (CL realmente)
DIV [SI].sectpista
INC AH ; numerado desde 1...
MOV sector,AH ; ...el resto es el sector
SHR AL,1
MOV cilindro,AL ; cilindro
RCL AL,1
AND AL,1
MOV cabezal,AL ; cabezal
MOV AL,sector
ADD AL,secciones
JC no_cabe ; sector+secciones > 255
DEC AX ; DEC AX = DEC AL
CMP AL,[SI].sectpista
JBE si_cabe
no_cabe: MOV status,4 ; 'sector no encontrado'
JMP fin_io
si_cabe: MOV AL,AH ; sector en AL
CBW ; sección 0 (AH = 0)
CALL pista0?
JZ s_xx ; sector físico en pista/cara 0
LEA BX,[SI].tabla_tsect-1
DEC AX ; AH = 0
resta_secc: INC BX
INC AH
MOV CL,[BX]
SUB CL,2
MOV CH,1
SHL CH,CL
SUB AL,CH
JNC resta_secc ; en las demás pistas
ADD AL,CH
XCHG AH,AL
s_xx: MOV sector,AL ; sector lógico convertido a
MOV seccion,AH ; sector y sección físicas
direct_acceso: CALL motor_ok ; asegurar que está en marcha
MOV AH,0
MOV sector_fin,AH ; no acceder a más de 1 sector
CALL pista0? ; (al menos de momento)
JNZ decide_multi ; no es pista 0
MOV AL,secciones
MOV secciones,AH ; las que restan (AH = 0)
JMP multi_proc
decide_multi: CMP [SI].multi_io,AH ; AH = 0
JNE io_pasos ; acceso sector a sector
CMP seccion,AH
JE multi_acc
CALL acceso_secc ; no acceso a inicio sector
JC fin_io
multi_acc: CMP secciones,AH ; AH = 0
JE fin_io
CALL num_secciones
MOV CL,AL
MOV AL,secciones ; AH = 0
DIV CL
AND AL,AL
JZ io_pasos ; no quedan sectores enteros
MOV secciones,AH ; las que restan
multi_proc: CALL acceso_multi ; de AL sectores
JC fin_io
io_pasos: CMP secciones,0
JE fin_io ; no restan secciones finales
CALL acceso_secc
JNC io_pasos
fin_io: CMP status,0 ; ZF = 1 -> operación correcta
RET
acceso_secc: PUSH AX
CMP orden,F_WRITE ; acabar transferencia sector
JE escritura
CMP orden,F_VERIFY
JE verificacion
CALL leido? ; realizar lectura...
JNC ya_leido ; sector ya en el buffer
hay_que_leer: CALL acceso_sector ; efectuar E/S
JC acc_ret ; ha habido fallo
ya_leido: CALL trans_secc ; buffer -> memoria
JMP acc_ret
escritura: CMP seccion,0
JNE prelectura ; sólo parte del sector cambia
CALL num_secciones
CMP secciones,AL
JAE escribir ; Todo el sector físico cambia
prelectura: CALL leido? ; Leer el sector físico para
JNC escribir ; cambiar sólo una parte de él
MOV orden,F_READ ; de momento leer...
CALL acceso_sector ; efectuar E/S
MOV orden,F_WRITE ; ... restaurar orden original
JC acc_ret ; ha habido fallo
escribir: CALL trans_secc ; memoria -> buffer
CALL acceso_sector ; volcar buffer al disco
JMP acc_ret
verificacion: PUSH BX
MOV BL,seccion
CALL num_secciones
dec_sec_veri: DEC secciones
JZ verifica
INC BX
CMP BL,AL
JB dec_sec_veri
verifica: POP BX
CALL acceso_sector ; leer para forzar verificación
acc_ret: PUSHF
INC sector ; preparado para otro sector
MOV seccion,0 ; desde su primera sección
POPF
POP AX
RET
acceso_multi: PUSH AX ; AL = sectores a transferir
MOV BX,ES ; desde 'sector' teniendo
SHL BX,4 ; cuidado con el DMA
ADD BX,DI
NEG BX ; BX = bytes hasta frontera DMA
CALL num_secciones
MOV CH,AL ; AL secciones de 512 bytes
MOV CL,0
SHL CX,1 ; CX = bytes por sector
XCHG AX,BX ; BL = secciones por sector
XOR DX,DX
DIV CX
MOV CL,AL ; CL = sectores que caben
POP AX ; AL = sectores a transferir
CMP AL,CL
JA acc_mult2 ; no hay problemas con el DMA
acc_mult1: MOV CL,AL
acc_mult2: AND CL,CL
JZ acc_mult3 ; primer sector problemático
MOV AH,sector
MOV sector_ini,AH
ADD AH,CL
DEC AH
MOV sector_fin,AH
INC AH
SUB AL,CL
CALL acceso_sector ; sectores no problemáticos
MOV sector,AH
JC acc_mult_fin
acc_mult3: AND AL,AL ; ahora el sector problemático
JZ acc_mult_fin
ADD secciones,BL ; compensar futuro decremento
CALL acceso_secc ; a través del buffer auxiliar
JC acc_mult_fin
DEC AL
JMP acc_mult1 ; sectores que restan
acc_mult_fin: RET
ejecuta_io ENDP
; ------------ Mover secciones desde el buffer hacia la memoria (con
; orden F_READ) después de la lectura o de la memoria al
; buffer (orden F_WRITE) antes de la escritura. En la
; verificación (orden F_VERIFY) no se mueve nada porque
; esta subrutina no es invocada.
trans_secc PROC
XPUSH <AX, BX, CX, SI> ; *
MOV BL,seccion ; desde esta sección
CALL num_secciones ; nº secciones del sector
otra_secci: PUSH BX
SHL BX,9 ; sección * 512
ADD BX,buffer ; dirección
MOV SI,BX
MOV CX,256 ; tamaño sección (palabras)
CALL swap_reg ; ¿intercambiar origen-destino?
REP MOVSW ; copiar 512 bytes
CALL swap_reg ; ¿intercambiar origen-destino?
POP BX
DEC secciones ; una menos
JZ fin_secc
INC BX ; otra sección del sector
CMP BL,AL ; ¿sector agotado?
JB otra_secci ; aún no
fin_secc: XPOP <SI, CX, BX, AX> ; *
RET
swap_reg: CMP CS:orden,F_WRITE
JE interc
CLC
RET
interc: XCHG SI,DI ; en escritura, invertir el
XPUSH <ES, DS> ; sentido de la operación
XPOP <ES, DS>
RET
trans_secc ENDP
; ------------ Comprobar si el sector ya está en el buffer.
leido? PROC
PUSH AX
MOV AL,buf_unidad
CMP AL,unidad
JNE no_leido ; es en otra unidad
MOV AL,cilindro
MOV AH,cabezal
CMP AX,buf_cilcab
JNE no_leido ; es en otro cilindro/cabezal
MOV AL,buf_sector
CMP AL,sector
JNE no_leido ; es otro sector
POP AX
RET ; está en el buffer
no_leido: STC
POP AX
RET ; sector no leído
leido? ENDP
; ------------ Leer o escribir sector(es). Se selecciona el tamaño de
; sector correcto antes de llamar a sector_io. En esta
; rutina se actualiza la variable «status» en función de
; los posibles errores de acceso. Si sector_fin es
; distinto de 0 se accede a los sectores indicados, si es
; 0 se accede sólo al sector «sector» a través del buffer
; intermedio y al final se anota el sector cargado ó
; escrito para evitar futuras lecturas innecesarias, a
; modo de mini-caché que dispara la velocidad de acceso a
; sectores lógicos consecutivos.
acceso_sector PROC
XPUSH <AX, BX>
CALL seek_drv ; posicionar el cabezal
JNC en_pista
CMP status,0 ; ¿error ya determinado?
JNE acc_fin_err
OR status,40h ; no: pues 'seek error'
acc_fin_err: STC
JMP acceso_fin
en_pista: CALL pista0?
MOV AL,2
JZ tam_acc_ok ; sectores 512 en cil./cab. 0
LEA BX,[SI].tabla_tsect
ADD BL,sector
ADC BH,0
MOV AL,[BX-1]
tam_acc_ok: MOV tsector,AL
CMP sector_fin,0 ; ¿usar buffer intermedio?
JE acceso_buffer
CALL sector_io
MOV sector_fin,0 ; no acceder a más de 1 sector
PUSHF ; **1
JMP acceso_rep ; en el futuro (por defecto)
acceso_buffer: XPUSH <ES, DI>
PUSH CS
POP ES
MOV DI,buffer ; acceso con buffer auxiliar
MOV AL,sector ; mismo sector inicial/final
MOV sector_ini,AL
MOV sector_fin,AL
CALL sector_io
MOV sector_fin,0
XPOP <DI, ES>
PUSHF ; **2
MOV AL,-1 ; invalidar contenido buffer
JC acceso_anota ; si hay error
CMP orden,F_VERIFY
JE acceso_rep ; nada leído físicamente
MOV AL,unidad
acceso_anota: MOV buf_unidad,AL
MOV AL,cilindro
MOV AH,cabezal
MOV buf_cilcab,AX
MOV AL,sector
MOV buf_sector,AL ; anotado el sector en buffer
acceso_rep: POPF ; ** mucho cuidado con la pila
CALL set_err ; ajustar variable «status»
acceso_fin: XPOP <BX, AX>
RET
acceso_sector ENDP
; ------------ Devolver el número de secciones del sector en curso.
num_secciones PROC
CALL pista0?
MOV AL,1
JZ num_secc_ok ; sectores 512 en cil./cab. 0
XPUSH <BX, CX>
LEA BX,[SI].tabla_tsect
ADD BL,sector
ADC BH,0
MOV CL,[BX-1]
SUB CL,2
MOV AL,1
SHL AL,CL
XPOP <CX, BX>
num_secc_ok: RET ; resultado en AL
num_secciones ENDP
; ------------ Asegurar que el motor está en marcha.
motor_ok PROC
PUSHA ; *
PUSH DS ; **
MOV BX,40h
PUSH BX
POP DS
MOV CH,255-18 ; CH = 255 - 1 segundo
CLI
MOV CL,CS:unidad
MOV AL,1
SHL AL,CL
TEST [BX-1],AL ; ¿motor en marcha?
JZ arrancarlo ; arrancarlo
CMP [BX],CH ; Si encendido y acelerado...
JBE ok_motor ; ...seguir
arrancarlo: OR [BX-1],AL ; arrancar motor
AND BYTE PTR [BX-1],0CFh ; borrar número unidad
MOV AL,CL
SHL AL,4 ; unidad << 4
OR [BX-1],AL ; nuevo número de unidad
MOV BYTE PTR [BX],255 ; asegurar que no se pare
STI
MOV DX,3F2h ; registro de salida digital
ADD CL,4
MOV AL,1
SHL AL,CL ; colocar bit del motor
OR AL,CS:unidad ; seleccionar unidad
OR AL,00001100b ; modo DMA, no hacer reset
OUT DX,AL ; poner en marcha el motor
MOV AX,90FDh
CLC
INT 15h ; permitir multitarea
JC ok_motor ; timeout
MOV AX,1000 ; 1 segundo aceleración
CALL retardo ; esperar aceleración disco
ok_motor: MOV [BX],CH ; cuenta máxima detención motor
STI ; sin forzar futura aceleración
POP DS ; **
POPA ; *
RET
motor_ok ENDP
; ------------ Establecer modalidad de operación del controlador
; y poner el motor en marcha. Si CF=1 se le da tiempo
; además a la unidad para que acelere.
reset_drv PROC
PUSHA
CALL motor_off_cnt ; cuenta detención motor
MOV CL,unidad
MOV AL,CL
SHL AL,2 ; unidad seleccionada
OR AL,1 ; bit de motor
SHL AL,CL ; colocar dicho bit
PUSH DS ; *
PUSH 40h
POP DS
CLI
MOV DS:[3Fh],AL
AND BYTE PTR DS:[3Eh],70h ; bit IRQ=0 y recalibrar
POP DS ; *
SHL AL,4 ; bits motor en nibble alto
OR AL,CL ; seleccionar unidad
OR AL,00001000b ; interrupciones+DMA y reset
MOV DX,3F2h ; registro de salida digital
OUT DX,AL ; señal de reset
CALL fdc_respiro ; tiempo reconocer reset en 486
OR AL,00000100b
OUT DX,AL ; fin de señal de reset
CALL espera_int ; rehabilitará interrupciones
MOV AL,8
CALL fdc_write ; comando 'leer estado int...'
CALL fdc_read
CALL fdc_read
CALL envia_specify ; comando 'specify' adecuado
POPA
RET
reset_drv ENDP
; ------------ Enviar comando specify a la controladora. El step-rate
; se selecciona según la densidad, para evitar un sonido
; extraño al posicionar o recalibrar el cabezal.
envia_specify PROC
PUSH AX
PUSH DS
PUSH 40h
POP DS
MOV AH,DS:[8Bh]
POP DS
MOV AL,3 ; comando 'specify'
CALL fdc_write
MOV AL,0BFh ; step rate para 500 kbps
AND AH,11000000b
JZ spec1_ok
MOV AL,0AFh ; step rate para 1 Mbps
CMP AH,11000000b
JE spec1_ok
MOV AL,0DFh ; step rate para 250/300 Kbps
spec1_ok: CALL fdc_write
MOV AL,2
CALL fdc_write ; head load y modo DMA
POP AX
RET
envia_specify ENDP
; ------------ Recargar cuenta para la detención del motor. Si CF=1 al
; entrar, se establece la mayor cuenta posible; en caso
; contrario, se pone el valor normal de la tabla base.
motor_off_cnt PROC
PUSHA
PUSH DS
MOV AL,0FFh ; valor máximo
JC motor_off_ok
PUSH 0
POP DS
LDS BX,DWORD PTR DS:[1Eh*4] ; DS:BX -> INT 1Eh
MOV AL,[BX+2] ; byte 2 tabla base disco
motor_off_ok: PUSH 40h
POP DS
MOV BYTE PTR DS:[40h],AL ; cuenta parada motor
POP DS
POPA
RET
motor_off_cnt ENDP
; ------------ Llevar el cabezal a la pista indicada, recalibrando si
; hubo un reset (se invocó la función 0 de la INT 13h o
; se ejecutó reset_drv) antes de esta operación. Primero
; se selecciona la velocidad de transferencia y se borra
; el resultado de cualquier operación anterior, para que
; todo quede listo para el próximo acceso a disco.
seek_drv PROC
PUSHA
CALL set_rate ; velocidad / borrar resultados
CALL envia_specify ; comando 'specify' adecuado
MOV AH,1
MOV CL,unidad
SHL AH,CL ; AH = 1 (A:) ó 2 (B:)
PUSH DS
PUSH 40h
POP DS
TEST AH,DS:[3Eh]
POP DS
JNZ do_seek ; la unidad ya fue recalibrada
CALL recalibrar
JC fallo_seek ; fallo al recalibrar
do_seek: MOV BX,94h
ADD BL,unidad
MOV AL,cilindro
PUSH DS ; *
PUSH 40h
POP DS
OR DS:[3Eh],AH ; unidad ya recalibrada
MOV AH,DS:[41h] ; código de error previo
CMP AL,[BX]
MOV [BX],AL
POP DS ; *
JNE hacer_seek ; seek necesario
CMP AH,40h ; ¿error de seek previo?
JNE seek_ok ; no, evitar seek innecesario
hacer_seek: MOV AL,0Fh
CALL fdc_write ; comando 'seek'
JC fallo_seek
MOV AL,cabezal
SHL AL,2
OR AL,unidad
CALL fdc_write ; enviar HD, US1, US0
MOV AL,cilindro
CALL fdc_write ; enviar cilindro
CALL espera_int ; esperar interrupción
JC fallo_seek
MOV AL,8
CALL fdc_write ; comando 'leer estado int...'
JC fallo_seek
CALL fdc_read ; leer registro de estado 0
JC fallo_seek
MOV AH,AL
CALL fdc_read ; leer cilindro actual
TEST AH,11000000b ; comprobar ST0
JNZ fallo_seek
MOV AL,15 ; estabilización para escritura
CMP orden,F_WRITE
JE rseek_ok
MOV AL,1 ; estabilización para lectura
rseek_ok: CBW ; AH = 0
CALL retardo ; esperar asentamiento cabezal
seek_ok: POPA
CLC ; retornar con éxito
RET
fallo_seek: POPA
STC ; retornar indicando fallo
RET
seek_drv ENDP
; ------------ Establecer velocidad de transferencia correcta si aún
; no ha sido seleccionada y borrar el resultado de otra
; operación previa.
set_rate PROC
PUSHA
CALL pista0?
MOV AX,[SI].vunidad ; velocidad pista 0 / demás
JZ vel_ok
MOV AL,AH
vel_ok: PUSH DS ; *
PUSH 40h
POP DS
MOV AH,DS:[8Bh]
SHR AH,6 ; aislar bits de velocidad
CMP AL,AH
JE vel_set ; velocidad ya seleccionada
MOV DX,3F7h
OUT DX,AL ; seleccionarla
SHL AL,6
AND BYTE PTR DS:[8Bh],00111111b
OR DS:[8Bh],AL
vel_set: POP DS ; *
LEA DI,status
MOV CX,8
borra_status: MOV [DI],CH ; borrar información de estado
INC DI
LOOP borra_status
POPA
RET
set_rate ENDP
; ------------ Recalibrar la unidad (si hay error se intenta otra vez
; para el caso de que deba moverse más de 77 pistas).
recalibrar PROC
PUSHA
MOV BX,94h
ADD BL,unidad
PUSH DS ; *
PUSH 40h
POP DS
MOV [BX],BH ; pista actual = 0
POP DS ; *
MOV CX,2 ; dos veces como mucho
recalibra: MOV AL,7
CALL fdc_write ; comando de 'recalibrado'
JC fallo_recal
MOV AL,cabezal
SHL AL,2
OR AL,unidad
CALL fdc_write ; enviar HD, US1, US0
JC fallo_recal
CALL espera_int ; esperar interrupción
JC fallo_recal
MOV AL,8
CALL fdc_write ; comando 'leer estado int...'
JC fallo_recal
CALL fdc_read ; leer registro de estado 0
JC fallo_recal
MOV AH,AL
CALL fdc_read ; leer cilindro actual
XOR AH,00100000b ; bajar bit de 'seek end'
TEST AH,11110000b ; comprobar resultado y ST0
JNZ fallo_recal ; sin 'seek end' o TRK0
MOV AX,1 ; pausa de 1 ms
CALL retardo
JMP recal_ret
fallo_recal: LOOP recalibra ; reintentar comando
STC ; condición de fallo
recal_ret: POPA
RET
recalibrar ENDP
; ------------ Cargar o escribir sector(es) del disco en ES:DI,
; actualizando la dirección en ES:DI pero sin alterar
; ningún otro registro. Si hay error se devuelve CF=1 y
; no se modifica ES:DI. A partir de fdc_result se dejan
; los 7 bytes que devuelve el FDC al final del acceso.
; En caso de verificación (F_VERIFY) se programa el DMA
; para que no realice transferencia física (convenio de
; las BIOS con fecha 15/11/85 y posterior).
sector_io PROC
XPUSH <AX, BX, CX, DX>
MOV CL,tsector
MOV CH,0
STC
RCL CH,CL
MOV CL,0 ; nº de bytes por sector
MOV AL,sector_fin
SUB AL,sector_ini
INC AX
CBW ; AX sectores (AH = 0)
MUL CX
MOV DX,AX ; bytes totales
MOV CX,AX
DEC CX ; bytes totales - 1
MOV AX,ES
CALL calc_dir_DMA ; AX:DI -> base BX y página AH
MOV AL,orden ; modo DMA necesario
CALL prepara_DMA
CMP AL,F_WRITE
MOV AL,11000101b ; comando de escritura del FDC
JE orden_io_ok
MOV AL,11100110b ; comando leer (verif.) del FDC
orden_io_ok: CALL fdc_write ; comando leer/escribir del FDC
JC sector_io_ko
MOV AL,cabezal
SHL AL,2
OR AL,unidad
CALL fdc_write ; byte 1 de la orden
MOV AL,cilindro
CALL fdc_write ; enviar cilindro
MOV AL,cabezal
CALL fdc_write ; enviar cabezal
MOV AL,sector_ini
CALL fdc_write ; enviar nº sector
MOV AL,tsector
CALL fdc_write ; longitud sector
MOV AL,sector_fin
CALL fdc_write ; último sector
MOV AL,[SI].gap
CALL fdc_write ; GAP de lectura/escritura
MOV AL,128
CALL fdc_write ; tamaño sector si longitud=0
CALL espera_int
PUSHF ; *
LEA BX,fdc_result
MOV CX,7
sect_io_res: CALL fdc_read ; leyendo resultados
MOV [BX],AL
INC BX
LOOP sect_io_res
POPF ; *
JC sector_io_ko
TEST fdc_result,11000000b
JNZ sector_io_ko
ADD DI,DX ; actualizar dirección
CLC ; Ok
JMP sector_io_fin
sector_io_ko: STC ; indicar fallo
sector_io_fin: XPOP <DX, CX, BX, AX>
RET
sector_io ENDP
; ------------ Devolver en AH la página de DMA y en BX la base. A la
; entrada, AX:DI -> dirección de memoria y CX = bytes-1.
; Se supone que el buffer no cruza una frontera de DMA.
calc_dir_DMA PROC
PUSH DX
MOV BX,16
MUL BX
ADD AX,DI
ADC DX,0 ; DX:AX = dirección 20 bits
MOV BX,AX ; base en BX
MOV AH,DL ; página
POP DX
RET
calc_dir_DMA ENDP
; ------------ Crear tabla con información para formatear. En ES:BX
; está el futuro sector de arranque del disquete.
genera_info PROC
PUSHA
MOV buf_unidad,-1 ; invalidar contenido buffer
MOV SI,buffer
MOV DI,BX
CALL pista0?
JNZ no_cilcab0 ; no es cilindro/cabezal 0
ADD DI,ES:[BX+70] ; DI -> datos pista 0
MOV CL,ES:[DI]
MOV CH,0 ; CX sectores en pista 0
INC DI
MOV AL,ES:[DI] ; GAP para pista 0
MOV AH,0 ; byte de relleno
INC DI
MOV BYTE PTR [SI],2 ; tamaño de sector
MOV BYTE PTR [SI+1],CL ; número de sectores
MOV [SI+2],AX ; GAP / byte de relleno
genera_0: ADD SI,4
MOV AL,cilindro
MOV AH,cabezal
MOV [SI],AX ; datos para cada sector
MOV AL,ES:[DI]
MOV AH,2 ; LOG2 (tamaño)-7
INC DI
MOV [SI+2],AX ; nº de sector / tamaño
LOOP genera_0
POPA
RET
no_cilcab0: ADD DI,ES:[BX+72]
CMP BYTE PTR ES:[BX+65],1
JE info_stv
MOV DL,ES:[DI+2] ; tamaño /F
MOV DH,ES:[DI] ; nº sectores
MOV [SI],DX
XCHG DH,DL ; tamaño en DH
MOV CL,DL
MOV CH,0 ; CX sectores
MOV AL,ES:[DI+1] ; GAP para formatear
MOV AH,0 ; byte relleno /F
MOV [SI+2],AX ; GAP / byte de relleno
PUSH DX
MOV AX,ES:[DI+3]
PUSH AX
ADD AL,AH
MUL cilindro
MOV DX,AX
POP AX
MUL cabezal
ADD AX,DX
XOR DX,DX
MOV BL,ES:[DI]
MOV BH,0
DIV BX ; DL = módulo
SUB DL,ES:[DI]
NEG DL
MOV AL,DL
POP DX ; restaurar tamaño en DH
MOV DL,AL ; primer sector de la pista - 1
MOV BL,ES:[DI] ; nº sectores en la pista
genera_pn: ADD SI,4
INC DX
CMP DL,BL
JBE ns_ok
MOV DL,1 ; empezar desde el 1
ns_ok: MOV AL,cilindro
MOV AH,cabezal
MOV [SI],AX ; datos para cada sector
MOV [SI+2],DX ; nº sector / LOG2 (tamaño)-7
LOOP genera_pn
POPA
RET
info_stv: MOV CH,ES:[DI] ; nº sectores
MOV CL,0 ; CL:CH sectores
MOV [SI],CX ; tamaño (CL=0) y número
XCHG CH,CL ; CX sectores
MOV AL,ES:[DI+1] ; GAP para formatear
MOV AH,4Eh ; byte de relleno /M
MOV [SI+2],AX ; GAP / byte de relleno
MOV DL,128
genera_otro: ADD SI,4
INC DX
MOV AL,cilindro
MOV AH,cabezal
MOV [SI],AX ; datos para cada sector
XPUSH <CX, DI> ; *
MOV CL,ES:[DI+2] ; CH está a 0
busca_num: ADD DI,3
CMP DL,ES:[DI]
MOV AX,ES:[DI+1] ; número de sector / tamaño
JE hallado ; es sector a cambiar número
LOOP busca_num
MOV AL,DL ; no cambiar número
MOV AH,0 ; e indicar tamaño 128
hallado: XPOP <DI, CX> ; *
MOV [SI+2],AX ; nº sector / LOG2 (tamaño)-7
LOOP genera_otro
POPA
RET
genera_info ENDP
; ------------ Formatear una pista.
formatea_pista PROC
PUSHA
MOV BX,buffer
MOV DI,BX
MOV CL,[BX+1]
MOV CH,0 ; CX sectores
SHL CX,2
DEC CX ; nº de bytes - 1
MOV AX,DS
CALL calc_dir_DMA ; AX:DI -> base BX y página AH
MOV AL,4Ah ; modo DMA para escribir
ADD BX,4 ; saltar primeros 4 bytes
CALL prepara_DMA
MOV BX,buffer
MOV AL,F_FORMAT
CALL fdc_write
JC fallo_fmt
MOV AL,cabezal
SHL AL,2
OR AL,unidad
CALL fdc_write ; byte 1 de la orden
JC fallo_fmt
MOV CX,4
format_cmd: MOV AL,[BX]
CALL fdc_write
INC BX
LOOP format_cmd
CALL espera_int
fallo_fmt: PUSHF
LEA BX,fdc_result
MOV CX,7
format_res: CALL fdc_read ; leyendo resultados
MOV [BX],AL
INC BX
LOOP format_res
POPF
JC fallo_format
TEST fdc_result,11000000b
JZ format_ret
fallo_format: STC ; fallo
format_ret: POPA
RET
formatea_pista ENDP
; ------------ Esperar interrupción de disquete durante casi 2
; segundos antes de considerar que ha sido un fracaso.
espera_int PROC
STI
PUSHA
XPUSH <DS, 40h>
POP DS
MOV AX,9001h
CLC
INT 15h ; permitir multitarea
MOV DX,0280h
MOV BX,3Eh
JC timeout_int
esp_int_1s: XOR CX,CX
esp_int: TEST [BX],DL ; ¿llegó la interrupción?
JNZ fin_espera
PMICRO
LOOP esp_int ; esperar durante casi 1 seg.
DEC DH
JNZ esp_int_1s
timeout_int: OR CS:status,DL ; timeout
STC
fin_espera: PUSHF
AND BYTE PTR [BX],7Fh ; para la próxima vez
POPF
POP DS
POPA
RET
espera_int ENDP
; ------------ Preparar DMA para E/S. A la entrada, BX = dirección de
; base, AH = registro de página y CX = nº bytes - 1.
prepara_DMA PROC
PUSH AX
CLI
OUT 0Bh,AL ; registro de modo del DMA
MOV AL,0
DELAY
OUT 0Ch,AL ; clear first/last flip-flop
MOV AL,BL
DELAY
OUT 4,AL
MOV AL,BH
DELAY
OUT 4,AL ; enviada dirección base
DELAY
MOV AL,AH
OUT 81h,AL ; registro de página del DMA
MOV AL,CL
DELAY
OUT 5,AL
MOV AL,CH
DELAY
OUT 5,AL ; enviada cuenta de bytes
STI
MOV AL,2
DELAY
OUT 0Ah,AL ; habilitar canal 2 de DMA
POP AX
RET
prepara_DMA ENDP
; ------------ Recibir byte del FDC en AL. A la vuelta, CF=1 si
; la operación fracasó (el FDC no estaba listo) y
; se indica la condición de timeout en «status».
fdc_read PROC
XPUSH <CX, DX, AX>
CALL fdc_respiro ; no abrasar el FDC
MOV DX,3F4h ; registro de estado del FDC
MOV CX,133 ; constante para 0,002 segundos
espera_rd: DELAY
IN AL,DX
AND AL,11000000b
CMP AL,11000000b ; ¿dato listo?
JE fdc_rd_ok
DELAY
IN AL,61h
AND AL,10h
CMP AL,AH
JE espera_rd ; reintentarlo durante 15,09 µs
MOV AH,AL
LOOP espera_rd
XPOP <AX, DX, CX>
OR status,80h ; timeout
MOV AL,0
STC ; fallo
RET
fdc_rd_ok: POP AX
INC DX ; apuntar al registro de datos
DELAY
IN AL,DX ; leer byte del FDC
XPOP <DX, CX>
CLC ; Ok
RET
fdc_read ENDP
; ------------ Enviar byte AL al FDC. A la vuelta, CF=1 si
; la operación fracasó (el FDC no estaba listo) y
; se indica la condición de timeout en «status».
fdc_write PROC
XPUSH <CX, DX, AX>
CALL fdc_respiro ; no abrasar el FDC
MOV DX,3F4h ; registro de estado del FDC
MOV CX,133 ; constante para 0,002 segundos
espera_wr: DELAY
IN AL,DX
TEST AL,80h ; ¿listo para E/S?
JNZ fdc_wr_ok
DELAY
IN AL,61h
AND AL,10h
CMP AL,AH
JE espera_wr ; reintentarlo durante 15,09 µs
MOV AH,AL
LOOP espera_wr
XPOP <AX, DX, CX>
OR status,80h ; timeout
STC ; fallo
RET
fdc_wr_ok: INC DX ; apuntar al registro de datos
POP AX
DELAY
OUT DX,AL ; enviar byte al FDC
XPOP <DX, CX>
CLC ; Ok
RET
fdc_write ENDP
; ------------ Retardo de 60 µs para dar tiempo al FDC en 486 rápidos.
fdc_respiro PROC
XPUSH <AX, CX>
MOV CX,4
fdc_ret: PMICRO
LOOP fdc_ret
XPOP <CX, AX>
RET
fdc_respiro ENDP
; ------------ Esperar exactamente AX milisegundos.
retardo PROC
PUSHF
PUSHA
MOV DX,16970 ; 16970 = 1193180/18*256/1000
MUL DX
MOV CL,AH ; dividir DX:AX entre 256 y
MOV CH,DL ; dejar el resultado en DX:CX
MOV DL,DH
MOV DH,0 ; DX:CX 15,09 µs-avos
retardando: PMICRO
LOOP retardando
AND DX,DX
JZ retardado
DEC DX
JMP retardando
retardado: POPA
POPF
RET
retardo ENDP
; --- Ubicación del sector de hasta 2048 bytes.
EVEN
buffer_io EQU $
tbuffer EQU 2048
fin_residente EQU $ ; fin del área residente sin contar el buffer
bytes_resid EQU fin_residente-ini_residente
; *****************************
; * *
; * I N S T A L A C I O N *
; * *
; *****************************
main PROC
ADD SP,2 ; quitar dirección de retorno
XPUSH <AX, BX, CX, DX, SI, DI, BP, DS, ES>
PUSH CS
POP DS
MOV WORD PTR interrupcion,531Eh ; opcode PUSH DS,BX
MOV BYTE PTR interrupcion+2,0BBh ; opcode MOV BX,??
PUSH CS
POP ES
CALL inic_general ; inicializar ciertas variables
CALL pc_ok?
TEST error,0FFFFh
JNZ exit_ins
CALL mx_get_handle ; obtener entrada Multiplex
JNC handle_ok
OR error,MX64FULL ; no quedan entradas
JMP exit_ins
handle_ok: MOV multiplex_id,AH ; entrada multiplex para 2M
CALL preservar_ints ; tomar nota de vectores
MOV AX,CS
LEA BX,buffer_io
CALL testDMA
JNC dma_ml_ok ; no hay problemas con el DMA
OR accion,BUFFERPLUS ; sí: aviso al usuario
MOV CX,dma_fix
ADD buffer,CX ; nuevo buffer
MOV AX,longitud_total
ADD AX,dma_fixp
MOV longitud_total,AX ; nuevo consumo de memoria
dma_ml_ok: CALL activar_ints ; interceptar vectores
exit_ins: CALL info
MOV BX,pcab_pet_segm
MOV ES,BX
MOV BX,pcab_pet_desp
MOV WORD PTR ES:[BX+3],100h ; indicar retorno correcto
MOV AX,longitud_total
MOV CL,4
SHL AX,CL
TEST error,0FFFFh
JZ exit_ok
MOV WORD PTR ES:[BX+14],0 ; OFFSET 0: no quedará
MOV WORD PTR ES:[BX+16],CS ; instalado en memoria
JMP exit_interr
exit_ok: MOV WORD PTR ES:[BX+14],AX ; OFFSET al último byte residente
MOV WORD PTR ES:[BX+16],CS
exit_interr: XPOP <ES, DS, BP, DI, SI, DX, CX, BX, AX>
RETF
main ENDP
;*********************************************************
;* *
;* SUBRUTINAS DE PROPOSITO GENERAL PARA LA INSTALACION *
;* *
;*********************************************************
; ------------ Inicializar ciertas variables.
inic_general PROC
MOV AX,(bytes_resid+tbuffer+15)/16
MOV longitud_total,AX ; memoria necesaria
MOV segmento_real,CS ; anotar segmento del bloque
MOV offset_real,0 ; ídem con el offset
MOV DL,0
CALL tipo_disco
JC err_drv
AND DL,DL
JZ err_drv ; no hay disqueteras
MOV info_A.tipo_drv,AL ; guardar tipo unidad A:
CMP DL,1
JE fin_inic ; no existe unidad B:
MOV DL,1
CALL tipo_disco
JC fin_inic
MOV info_B.tipo_drv,AL ; guardar tipo unidad B:
RET
err_drv: MOV AX,MALBIOS
OR error,AX
fin_inic: RET
tipo_disco: PUSH ES
MOV AH,8
MOV BL,0
INT 13h
JC tipo_dsk_err
MOV AL,BL
AND AL,AL
JZ tipo_dsk_err
CMP AL,4
JB tipo_dsk_ok
MOV AL,4 ; 1.44/2.88 indistinto
tipo_dsk_ok: CLC
POP ES
RET
tipo_dsk_err: STC
POP ES
RET
inic_general ENDP
; ------------ Preservar vectores de interrupción previos.
preservar_INTs PROC
XPUSH <ES, DI>
LEA DI,tabla_vectores
MOV CL,[DI-1]
MOV CH,0 ; CX vectores interceptados
otro_vector: XPUSH <CX, DI>
MOV AH,35h
MOV AL,[DI]
INT 21h ; obtener vector de INT xx
XPOP <DI, CX>
MOV [DI+1],BX ; anotar donde apunta
MOV [DI+3],ES
ADD DI,5
LOOP otro_vector ; repetir con los restantes
XPOP <DI, ES>
RET
preservar_INTs ENDP
; ------------ desviar vectores de interrupción a las nuevas rutinas.
activar_INTs PROC
LEA SI,offsets_ints
MOV CX,CS:[SI] ; CX vectores a desviar
ADD SI,2
desvia_otro: MOV AL,CS:[SI] ; número del vector en curso
MOV DX,CS:[SI+1] ; obtener offset
MOV AH,25h
INT 21h ; desviar INT xx a DS:DX
ADD SI,3
LOOP desvia_otro
RET
activar_INTs ENDP
; ------------ Buscar entrada no usada en la interrupción Multiplex.
; A la salida, CF=1 si no hay hueco (ya hay 64 programas
; residentes instalados con esta técnica). Si CF=0, se
; devuelve en AH un valor de entrada libre en la INT 2Fh.
mx_get_handle PROC
MOV AH,0C0h
mx_busca_hndl: PUSH AX
MOV AL,0
INT 2Fh
CMP AL,0FFh
POP AX
JNE mx_si_hueco
INC AH
JNZ mx_busca_hndl
STC
RET
mx_si_hueco: CLC
RET
mx_get_handle ENDP
; ------------ Inicializar variable idioma_sp según idioma del país.
habla_hispana? PROC
XPUSH <AX, BX, CX, DX, BP>
MOV AH,30h
INT 21h
XCHG AH,AL ; AX = versión del DOS
MOV BP,AX
MOV idioma_sp,OFF ; supuesto de habla no hispana
CMP BP,200h
JB habla_ok
LEA DX,buffer_aux
MOV AX,3800h
INT 21h ; obtener información del pais
CMP BP,20Bh
JE habla_ax ; DOS 2.11: AX cód. telefónico
CMP BP,300h
JB habla_ok ; 2.x excepto 2.11: mala suerte
MOV AX,BX
habla_ax: LEA BX,paises_sp-2
MOV CX,numpaises_sp
habla_sp?: ADD BX,2
CMP AX,[BX]
JE habla_hispana
LOOP habla_sp?
habla_ok: XPOP <BP, DX, CX, BX, AX>
RET
habla_hispana: MOV idioma_sp,ON ; país de habla hispana
XPOP <BP, DX, CX, BX, AX>
RET
habla_hispana? ENDP
; ------------ Imprimir cadena en DS:DX delimitada por un 0 ó un 255.
; Si hay que imprimir en inglés se toma la cadena que va
; después si ésta acaba en 255 (si acaba en 0, no hay
; distinción entre mensaje castellano e inglés).
print PROC
XPUSH <AX, BX, CX, DX>
pr_decidir: CMP idioma_sp,OFF
JE usar_uk
CMP idioma_sp,ON
JE usar_sp
PUSH DX
CALL habla_hispana? ; determinar lengua
POP DX
JMP pr_decidir
usar_uk: MOV BX,DX
DEC BX
usar_uk?: INC BX
CMP BYTE PTR [BX],0
JE usar_sp ; acaba en 0: no traducir
CMP BYTE PTR [BX],255
JNE usar_uk?
LEA DX,[BX+1] ; acaba en 255: traducir
usar_sp: MOV BX,DX
DEC BX
print_cad: INC BX
CMP BYTE PTR [BX],0
JE prlong_ok
CMP BYTE PTR [BX],255
JNE print_cad ; calcular longitud
prlong_ok: MOV CX,BX
SUB CX,DX
MOV AH,40h
MOV BX,1
INT 21h
XPOP <DX, CX, BX, AX>
RET
print ENDP
; ------------ Informar al usuario.
info PROC
LEA DX,instalado_txt
TEST error,0FFFFh
JZ info_ins_ok
LEA DX,noinstall_txt
info_ins_ok: CALL print
LEA DX,err_malpc
TEST error,MALPC
JNZ fin_info
LEA DX,err_maldos
TEST error,MALDOS
JNZ fin_info
LEA DX,err_malbios
TEST error,MALBIOS
JNZ fin_info
LEA DX,err_maldrv
TEST error,MALDRV
JNZ fin_info
LEA DX,err_mx64full
TEST error,MX64FULL
JZ info_war
fin_info: CALL print
info_war: TEST accion,BUFFERPLUS
JZ no_warn
LEA DX,dma_cross_txt
CALL print
no_warn: RET
info ENDP
; ------------ Comprobar que la configuración es la adecuada. Para
; saber si la INT 13h de este ordenador acaba llamando a
; la INT 40h, se desvía la INT 40h y se provoca un inocuo
; reset de disquetes vía INT 13h para comprobar si pasa
; por la INT 40h.
pc_ok? PROC
CALL test_i40
TEST accion,I40
JZ test_dos ; no soportada la INT 40h
MOV nueva_i13,40h
MOV vieja_i13,40h ; usar INT 40 en vez de INT 13
test_dos: MOV AH,30h
INT 21h
XCHG AH,AL
CMP AX,31Eh ; ¿DOS 3.30 o superior?
MOV AX,MALDOS
JB pc_nok
CALL testAT
MOV AX,MALPC
JC pc_nok
TEST error,MALBIOS
JNZ pc_ok ; con ese error vale
MOV AX,MALDRV
CMP info_A.tipo_drv,2 ; ¿unidad A: de 1.2?
JE pc_ok
CMP info_A.tipo_drv,4 ; ¿unidad A: de 1.44 ó 2.88?
JAE pc_ok
CMP info_B.tipo_drv,2 ; ¿unidad B: de 1.2?
JE pc_ok
CMP info_B.tipo_drv,4 ; ¿unidad B: de 1.44 ó 2.88?
JAE pc_ok
pc_nok: OR error,AX
pc_ok: RET
pc_ok? ENDP
; --- Comprobar si la INT 40h está en uso
test_i40: XPUSH <DS, ES> ; *
MOV AX,3540h
INT 21h
XPUSH <ES, BX> ; vector de INT 40h original
LEA DX,i40_aux
MOV AX,2540h
INT 21h ; establecer nueva INT 40h
XOR AX,AX
MOV DL,0
INT 13h ; reset de disco
XPOP <DX, DS>
MOV AX,2540h
INT 21h ; restaurar INT 40h original
XPOP <ES, DS> ; *
RET
i40_aux PROC
OR CS:accion,I40 ; sí utilizada INT 40h
IRET ; desde la INT 13h
i40_aux ENDP
; --- Detectar 286 ó superior.
testAT PROC
PUSH AX
PUSHF
POP AX
OR AH,70h ; intentar activar bit 12, 13 ó 14
PUSH AX ; del registro de estado
POPF
PUSHF
POP AX
AND AH,0F0h
CMP AH,0F0h
JE testedAT
STC
testedAT: CMC ; CF = 0 en AT y 1 en PC/XT
POP AX
RET
testAT ENDP
; ------------ Comprobar que el buffer para el DMA en la copia
; residente no cruza una frontera. En ese caso se emplea
; otro buffer ubicado tras el habitual, lo que aumenta el
; consumo de memoria de este área. A la entrada AX apunta
; al segmento que contendrá el buffer y BX el offset. Si
; se produce el cruce, dma_fix devuelve la cuantía en que
; hay que incrementar (en bytes) la dirección base para
; evitarlo y dma_fixp también (pero en párrafos).
testDMA PROC
XPUSH <AX, CX, DX>
MOV CX,16
MUL CX
ADD AX,BX
ADC DX,0 ; DX:AX = dirección 20 bits
MOV CX,DX
PUSH AX
ADD AX,tbuffer-1 ; buffer para el mayor sector
ADC DX,0
POP AX
CMP DX,CX
JE dmatested
NEG AX
MOV dma_fix,AX
ADD AX,15
MOV CL,4
SHR AX,CL
MOV dma_fixp,AX
STC ; CF=1 -> cruza frontera
dmatested: XPOP <DX, CX, AX>
RET
testDMA ENDP
; ***********************************************
; * *
; * D A T O S N O R E S I D E N T E S *
; * *
; ***********************************************
ON EQU 1 ; constantes booleanas
OFF EQU 0
; ------------ Gestión de memoria y control de instalación.
dma_fix DW ? ; bytes para alargar buffer (por DMA)
dma_fixp DW ? ; párrafos
offsets_ints DW 2 ; número de vectores interceptados
nueva_i13 DB 13h ; tabla de offsets de los vectores
DW ges_int13 ; de interrupción interceptados
DB 2Fh
DW ges_int2F
MALPC EQU 1 ; Códigos de error
MALDOS EQU 2
MALBIOS EQU 4
MALDRV EQU 8
MX64FULL EQU 128
BUFFERPLUS EQU 8 ; códigos de acción e información
I40 EQU 16
error DW 0 ; variable para acumular errores
accion DB 0 ; variable que indica lo sucedido
; ------------ Códigos de modos y órdenes del DMA y del FDC.
F_READ EQU 46h ; modo DMA para lectura
F_WRITE EQU 4Ah ; modo DMA para escritura
F_VERIFY EQU 42h ; modo DMA para verificación
F_FORMAT EQU 01001101b ; orden de formateo del FDC
; ------------ Otras variables.
idioma_sp DB 5Ah ; ni en ON ni en OFF al principio
; --- Código telefónico de países de
; habla hispana (mucha o poca).
paises_sp DW 54 ; Argentina
DW 591 ; Bolivia
DW 57 ; Colombia
DW 506 ; Costa Rica
DW 56 ; Chile
DW 593 ; Ecuador
DW 503 ; El Salvador
DW 34 ; España
DW 63 ; Filipinas
DW 502 ; Guatemala
DW 504 ; Honduras
DW 212 ; Marruecos
DW 52 ; México
DW 505 ; Nicaragua
DW 507 ; Panamá
DW 595 ; Paraguay
DW 51 ; Perú
DW 80 ; Puerto Rico
DW 508 ; República Dominicana
DW 598 ; Uruguay
DW 58 ; Venezuela
DW 3 ; genérico latinoamérica
numpaises_sp EQU ($-OFFSET paises_sp)/2
; ------------ Texto.
instalado_txt DB 13,10,"2M 2.1 instalado.",13,10,255
DB 13,10,"2M 2.1 installed.",13,10,0
noinstall_txt DB 13,10,"2M 2.1 no instalado.",13,10,255
DB 13,10,"2M 2.1 not installed.",13,10,0
err_malpc DB " - Error: Necesario ordenador AT. Utilice 2MX en esta máquina.",13,10,7,255
DB " - Error: Needs AT system. Use 2MX on this computer.",13,10,7,0
err_mx64full DB " - Error: Ya hay 64 programas residentes con la misma técnica.",13,10,7,255
DB " - Error: There are already 64 TSR's with the same technique.",13,10,7,0
err_maldos DB " - Error: necesaria versión DOS 3.30 ó posterior.",13,10,7,255
DB " - Error: needs at least DOS 3.30 or above.",13,10,7,0
err_malbios DB " - Error: No puedo detectar el tipo de las unidades. Instale 2M-ABIOS antes.",13,10,255
DB " - Error: Impossible to detect drive types. Please install 2M-ABIOS before.",13,10,0
err_maldrv DB " - Error: Necesaria(s) unidad(es) de alta densidad.",13,10,255
DB " - Error: Needs high-density floppy drive(s).",13,10,0
dma_cross_txt DB " - Nota: El buffer de E/S cruzaba una frontera de DMA y fue ampliado.",13,10
DB " Cambie la ubicación en memoria de 2M para ahorrar unos bytes.",13,10,255
DB " - Note: I/O buffer has been extended because 2M crosses a DMA boundary.",13,10
DB " Modify the memory location of 2M to save a little memory.",13,10,0
buffer_aux DB 64 DUP (0) ; buffer para alguna función del DOS
_PRINCIPAL ENDS
END